package com.xsw.common.utils.mini;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import com.alibaba.fastjson2.JSONObject;
import com.xsw.common.constant.CacheConstants;
import com.xsw.common.core.domain.model.MiniLoginBody;
import com.xsw.common.core.domain.model.WeChatTemplateMsg;
import com.xsw.common.exception.ServiceException;
import com.xsw.common.utils.LogUtils;
import com.xsw.common.utils.http.HttpUtils;
import me.chanjar.weixin.common.error.WxErrorException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author yjc
 * @create 2023-02-15 8:49
 */
@Component
public class MiniUtils {
    private final Logger log = LoggerFactory.getLogger(MiniUtils.class);


    @Resource
    private WxMaService wxService;

    @Value("${miniapp.configs.appid}")
    private String miniAppid;

    @Value("${miniapp.configs.secret}")
    private String miniAppkey;

    private String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&";

    private String sendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";


    //微信小程序发送订阅消息
    public void sendSmallMsg(String msgType, String openId, String msg, String userName, String PAGES_ZP, String reason) {
//        LogUtils.getBlock("wxService:{}", wxService);
//        LogUtils.getBlock("templteId:{}", templteId);
        Map<String, String> map = new HashMap<>();
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm");
        map.put("name2", msgType);
        map.put("phrase5", msg);
        map.put("time17", format.format(date));
        map.put("thing16", userName);
        map.put("thing10", reason);
        WxMaSubscribeMessage wxMaSubscribeMessage = WxMaSubscribeMessage.builder()
                .toUser(openId)
//                .templateId(templteId)
                .miniprogramState(WxMaConstants.MiniProgramState.DEVELOPER)
                .page(PAGES_ZP)
                .build();
        // 设置将推送的消息
        map.forEach((k, v) -> {
            wxMaSubscribeMessage.addData(new WxMaSubscribeMessage.Data(k, v));
        });
        try {
            LogUtils.getBlock("开始发送消息！！！！");
            wxService.getMsgService().sendSubscribeMsg(wxMaSubscribeMessage);
            LogUtils.getBlock("消息发送成功！！！！");
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
    }

    public JSONObject getSessionKeyOrOpenId(String code) {
        String wxUrl = "https://api.weixin.qq.com/sns/jscode2session";
        String appId = miniAppid;
        String appScrect = miniAppkey;
        String openid = null;
        String url = wxUrl + "?appid=" + appId + "&secret=" + appScrect + "&js_code=" + code + "&grant_type=authorization_code";

        String content = reuqestGet(url);

        JSONObject jsonObject = JSONObject.parseObject(content);

//        System.out.println(jsonObject);
//        System.out.println(jsonObject.getString("openid"));
//        LogUtils.getBlock("jsonObject:{}", jsonObject);

        return jsonObject;
    }

    /**
     * 手机号
     *
     * @param sessionKey 微信sessionKey
     * @param params     微信小程序自动登录参数
     * @return token
     */
    @Transactional(rollbackFor = Exception.class)
    public String getPhone(String sessionKey, MiniLoginBody params) {

        String encryptedData = params.getEncryptedData();
        String iv = params.getIv();
        JSONObject userInfo = this.getUserInfo(encryptedData, sessionKey, iv);
        LogUtils.getBlock("联合登陆返回：" + userInfo.toString());

        if (userInfo.containsKey("purePhoneNumber")) {
            String phone = (String) userInfo.get("purePhoneNumber");
            return phone;
        }

        return "";
    }

    /**
     * 解密，获取微信信息
     *
     * @param encryptedData 加密信息
     * @param sessionKey    微信sessionKey
     * @param iv            微信揭秘参数
     * @return 用户信息
     */
    public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {

        log.info("encryptedData:{},sessionKey:{},iv——{}" + encryptedData, sessionKey, iv);
        //被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        //加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
        //偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            //如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            //初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            //初始化
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, StandardCharsets.UTF_8);
                return JSONObject.parseObject(result);
            }
        } catch (Exception e) {
            log.error("解密，获取微信信息错误:{}", e);
        }
        throw new ServiceException("联合第三方登录，授权信息错误");
    }

    public String reuqestGet(String url) {
        String result = "";
        BufferedReader in = null;
        try {
            URL realUrl = new URL(url);
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
//            LogUtils.getBlock("map:{}", map);
            // 遍历所有的响应头字段
//         for (String key : map.keySet()) {
//             System.out.println(key + "--->" + map.get(key));
//         }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
//                LogUtils.getBlock("line:{}", line);
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常！" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }


        return result;
    }

    /**
     * 获取微信小程序授权码
     *
     * @param stringRedisTemplate
     * @return
     */
    public String getAccessToken(StringRedisTemplate stringRedisTemplate) {

        BoundValueOperations<String, String> ops = stringRedisTemplate.boundValueOps(CacheConstants.MINI_ACCESS_TOKEN_CACHE_PREFIX);
        String tokenRedis = ops.get();
        if (StringUtils.isBlank(tokenRedis)) {
            //Redis中token过期，需要重新获取
            LogUtils.getBlock("Redis中access_token过期，需要重新获取");

            LogUtils.getBlock("weChatAppid:" + miniAppid);
            LogUtils.getBlock("weChatAppkey:" + miniAppkey);
            String requestUrl = accessTokenUrl + "appid=" + miniAppid + "&secret=" + miniAppkey;
            String resp = HttpUtils.sendGet(requestUrl);
            JSONObject result = JSONObject.parseObject(resp);
            tokenRedis = "" + result.get("access_token");
            Integer expiresIn = (Integer) result.get("expires_in");
            //缓存新的token
            ops.set(tokenRedis, Long.parseLong(expiresIn.toString()), TimeUnit.SECONDS);
        }

        return tokenRedis;
    }


    public BufferedImage generateQRCode(String content, String envVersion, StringRedisTemplate stringRedisTemplate) throws Exception {
        String accessToken = getAccessToken(stringRedisTemplate);

        Map<String, Object> queryMap = new HashMap<>();
        // 增加二维码链入小程序环境的区分.    要打开的小程序版本。正式版为 "release"，体验版为 "trial"，开发版为 "develop"。默认是正式版。
        // 如果是测试环境,生成体验版小程序
        queryMap.put("page", "pages_pay_parking_fees/pages_pay_parking_fees");
        queryMap.put("scene", content);
        queryMap.put("check_path", false);
        queryMap.put("env_version", envVersion);
        // 调用接口,获取小程序image
        byte[] byteArr = getWechatQrcodeByOkhttp3("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken, queryMap);
        ByteArrayInputStream bis = new ByteArrayInputStream(byteArr);
        BufferedImage bufferedImage = ImageIO.read(bis);

        if (bufferedImage != null) {
            log.error("小程序码获取成功");
        } else {
            log.info("bufferedImage：{}", bufferedImage);
            throw new ServiceException("小程序码生成失败");
        }
        return bufferedImage;

    }

    private byte[] getWechatQrcodeByOkhttp3(String url, Map<String, Object> body) {
        OkHttpClient client = new OkHttpClient().newBuilder().build();
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
        RequestBody requestBody = RequestBody.create(mediaType, com.alibaba.fastjson.JSONObject.toJSONString(body));
        Request request = new Request.Builder().url(url).method("POST", requestBody).build();
        try {
            Response response = client.newCall(request).execute();
            log.info("response:{}", response);
            if (response.isSuccessful()) {
                return response.body().bytes();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
